winsafe\gui\native_controls/combo_box.rs
1use std::any::Any;
2use std::marker::PhantomPinned;
3use std::pin::Pin;
4use std::sync::Arc;
5
6use crate::co;
7use crate::decl::*;
8use crate::gui::{collections::*, events::*, privs::*, *};
9use crate::prelude::*;
10
11struct ComboBoxObj {
12 base: BaseCtrl,
13 events: ComboBoxEvents,
14 _pin: PhantomPinned,
15}
16
17native_ctrl! { ComboBox: ComboBoxObj => ComboBoxEvents;
18 /// Native
19 /// [combo box](https://learn.microsoft.com/en-us/windows/win32/controls/about-combo-boxes)
20 /// control.
21}
22
23impl ComboBox {
24 /// Instantiates a new `ComboBox` object, to be created on the parent window
25 /// with [`HWND::CreateWindowEx`](crate::HWND::CreateWindowEx).
26 ///
27 /// # Panics
28 ///
29 /// Panics if the parent window was already created – that is, you cannot
30 /// dynamically create a `ComboBox` in an event closure.
31 ///
32 /// Panics if vertical resizing behavior is
33 /// [`Vert::Resize`](crate::gui::Vert::Resize).
34 ///
35 /// # Examples
36 ///
37 /// ```no_run
38 /// use winsafe::{self as w, prelude::*, gui};
39 ///
40 /// let wnd: gui::WindowMain; // initialized somewhere
41 /// # let wnd = gui::WindowMain::new(gui::WindowMainOpts::default());
42 ///
43 /// let cmb = gui::ComboBox::new(
44 /// &wnd,
45 /// gui::ComboBoxOpts {
46 /// position: (10, 10),
47 /// width: 140,
48 /// items: vec![
49 /// "Avocado".to_owned(),
50 /// "Banana".to_owned(),
51 /// "Grape".to_owned(),
52 /// "Orange".to_owned(),
53 /// ],
54 /// selected_item: Some(0),
55 /// ..Default::default()
56 /// },
57 /// );
58 /// ```
59 #[must_use]
60 pub fn new(parent: &(impl GuiParent + 'static), opts: ComboBoxOpts) -> Self {
61 if opts.resize_behavior.1 == Vert::Resize {
62 panic!("ComboBox cannot be resized with Vert::Resize.");
63 }
64
65 let ctrl_id = auto_id::set_if_zero(opts.ctrl_id);
66 let new_self = Self(Arc::pin(ComboBoxObj {
67 base: BaseCtrl::new(ctrl_id),
68 events: ComboBoxEvents::new(parent, ctrl_id),
69 _pin: PhantomPinned,
70 }));
71
72 let self2 = new_self.clone();
73 let parent2 = parent.clone();
74 parent
75 .as_ref()
76 .before_on()
77 .wm(parent.as_ref().wnd_ty().creation_msg(), move |_| {
78 self2.0.base.create_window(
79 opts.window_ex_style,
80 "COMBOBOX",
81 None,
82 opts.window_style | opts.control_style.into(),
83 opts.position.into(),
84 SIZE::with(opts.width, 0),
85 &parent2,
86 );
87 ui_font::set(self2.hwnd());
88 self2.items().add(&opts.items)?;
89 self2.items().select(opts.selected_item);
90 parent2
91 .as_ref()
92 .add_to_layout(self2.hwnd(), opts.resize_behavior);
93 Ok(0) // ignored
94 });
95
96 new_self
97 }
98
99 /// Instantiates a new `ComboBox` object, to be loaded from a dialog
100 /// resource with [`HWND::GetDlgItem`](crate::HWND::GetDlgItem).
101 ///
102 /// # Panics
103 ///
104 /// Panics if the parent dialog was already created – that is, you cannot
105 /// dynamically create a `ComboBox` in an event closure.
106 ///
107 /// Panics if vertical resizing behavior is
108 /// [`Vert::Resize`](crate::gui::Vert::Resize).
109 #[must_use]
110 pub fn new_dlg(
111 parent: &(impl GuiParent + 'static),
112 ctrl_id: u16,
113 resize_behavior: (Horz, Vert),
114 ) -> Self {
115 if resize_behavior.1 == Vert::Resize {
116 panic!("ComboBox cannot be resized with Vert::Resize.");
117 }
118
119 let new_self = Self(Arc::pin(ComboBoxObj {
120 base: BaseCtrl::new(ctrl_id),
121 events: ComboBoxEvents::new(parent, ctrl_id),
122 _pin: PhantomPinned,
123 }));
124
125 let self2 = new_self.clone();
126 let parent2 = parent.clone();
127 parent.as_ref().before_on().wm_init_dialog(move |_| {
128 self2.0.base.assign_dlg(&parent2);
129 parent2
130 .as_ref()
131 .add_to_layout(self2.hwnd(), resize_behavior);
132 Ok(true) // ignored
133 });
134
135 new_self
136 }
137
138 /// Item methods.
139 #[must_use]
140 pub const fn items(&self) -> ComboBoxItems<'_> {
141 ComboBoxItems::new(self)
142 }
143}
144
145/// Options to create a [`ComboBox`](crate::gui::ComboBox) programmatically with
146/// [`ComboBox::new`](crate::gui::ComboBox::new).
147pub struct ComboBoxOpts {
148 /// Left and top position coordinates of control within parent's client
149 /// area, to be
150 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
151 ///
152 /// Defaults to `gui::dpi(0, 0)`.
153 pub position: (i32, i32),
154 /// Control width to be
155 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
156 ///
157 /// Defaults to `gui::dpi_x(120)`.
158 pub width: i32,
159 /// Combo box styles to be
160 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
161 ///
162 /// Defaults to `CBS::DROPDOWNLIST`.
163 ///
164 /// Suggestions:
165 /// * replace with `CBS::DROPDOWN` to allow the user to type a text;
166 /// * add `CBS::SORT` to automatically sort the items.
167 pub control_style: co::CBS,
168 /// Window styles to be
169 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
170 ///
171 /// Defaults to `WS::CHILD | WS::GROUP | WS::TABSTOP | WS::VISIBLE`.
172 pub window_style: co::WS,
173 /// Extended window styles to be
174 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
175 ///
176 /// Defaults to `WS_EX::LEFT`.
177 pub window_ex_style: co::WS_EX,
178
179 /// The control ID.
180 ///
181 /// Defaults to an auto-generated ID.
182 pub ctrl_id: u16,
183 /// Horizontal and vertical behavior of the control when the parent window
184 /// is resized.
185 ///
186 /// **Note:** A `ComboBox` cannot be resized vertically, so it will panic if
187 /// you use `Vert::Resize`.
188 ///
189 /// Defaults to `(gui::Horz::None, gui::Vert::None)`.
190 pub resize_behavior: (Horz, Vert),
191
192 /// Items to be added.
193 ///
194 /// Defaults to none.
195 pub items: Vec<String>,
196 /// Index of the item initially selected. The item must exist.
197 ///
198 /// Defaults to `None`.
199 pub selected_item: Option<u32>,
200}
201
202impl Default for ComboBoxOpts {
203 fn default() -> Self {
204 Self {
205 position: dpi(0, 0),
206 width: dpi_x(120),
207 control_style: co::CBS::DROPDOWNLIST,
208 window_style: co::WS::CHILD | co::WS::GROUP | co::WS::TABSTOP | co::WS::VISIBLE,
209 window_ex_style: co::WS_EX::LEFT,
210 ctrl_id: 0,
211 resize_behavior: (Horz::None, Vert::None),
212 items: Vec::new(),
213 selected_item: None,
214 }
215 }
216}